Downloading Content

Latest update: June 2013

In this tutorial, we will show you how to download content from your FlashAir device. We will take the information from the content list that we created using command.cgi and make an application that downloads and displays a selected image file.

This tutorial follows iOS Tutorial 2: Getting A List of Contents in the FlashAir iOS app tutorial series.
Let's try making this app.

Creating the Screen Layout

Here is the screen layout that we would like to create:

image shows the screen layout for this app

Once the application is launched, the list should display the contents of the current folder in the TableView block, the number of files in the labelCount block and the name of the directory to the labelDirectory block.

We will add the following elements:

Content List Screen

  • Round Rect Button ( UIButton)
    • back: Moves you up one directory
  • Label ( UILabel)
    • labelCount: Shows the number of files in the current folder
    • labelDirectory: Shows the current folder in the path
  • Table View ( UITableView)
    • A contents list
  • Table View Cell ( UITableViewCell)
    • Individual cell content of the list

Image Display Screen

  • View Controller ( UIViewController)
  • Image View ( UIImageView)
    • Shows the image file

Other

  • Navigation Controller ( UINavigationController)
    • Used to move between showing image files on the display screen and the file contents on the list screen

We will create a content list that looks like this:

images shows the format of the content list

If a folder name in the list is tapped, we will show the contents of that folder.
If "back" is tapped, we will move one element up the path and display the contents of that folder.
If an image file in the list is tapped, we will display the image like this:

image shows how image files should be displayed

Writing Code

Displaying Image Files

Image files are displayed using a ViewController. The ViewController is set in the class:

  • FSImageViewController

An image file can be acquired without using CGI commands if you specify the file path directly (in a browser or file explorer). To get data from the image file, we will use the variable NSData dataWithContentsOfURL . This function will return the data object.

FSImageViewController.h

@interface FSImageViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIImageView *imageView;
@property (nonatomic) NSString *fileInfo;
@end
  • Line 3:
    fileInfo: is a property of View Controller that stores the content list file information

FSImageViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Do any additional setup after loading the view.
    // Make a file path
    NSString *dir = 
        [[self.fileInfo componentsSeparatedByString:@","] objectAtIndex:0];
    NSString *filename = 
        [[self.fileInfo componentsSeparatedByString:@","] objectAtIndex:1];
    NSString *filePath = 
        [[dir stringByAppendingString:@"/"] stringByAppendingString:filename];
    // Run                    
    NSURL *url = 
        [NSURL URLWithString:[NSString stringWithFormat:@"http://flashair/%@", filePath]];
    // Get image data
    if(nil == url)return;
    NSData *img_data = [NSData dataWithContentsOfURL:url];
    UIImage *img = [[UIImage alloc] initWithData:img_data];
    // Display results
    self.imageView.image = img;

}
  • Lines 7-12:
    We create the path to the file by using the information that was retrieved from the content list`s View Controller.
  • Lines 14-15:
    We set the URL to make the request.
  • Lines 17-19:
    Retrieves the image data from the URL and creates the image.
  • Line 21:
    We have set the image data we acquired to the Image View.

Display the Content List

To get the content list, we use command.cgi with op=100 and op=101.
For information on how to get the contents list, please see iOS Tutorial 2: Getting a Content List.
We will set the results of the cgi command to a Table View.

FSViewController.h

#import <UIKit/UIKit.h>
#import "FSImageViewController.h"

@interface FSViewController : UIViewController{
@private
    NSArray  *files;
    NSString *count;
    NSString *rowdata;
}
@property (strong, nonatomic) IBOutlet UILabel *labelCount;
@property (strong, nonatomic) IBOutlet UILabel *labelDirectory;
@property (strong, nonatomic) IBOutlet UITableView *tableViewFileList;
- (IBAction)buttonPush:(id)sender;
- (void)getFileList:(NSString*)path;
@end
  • Line 2:
    We need to import FSImageViewController.h so that when you click an item in the list, the rowdata property of FSViewController will be sent to the ImageViewController that fetches and displays the image. (The value of rowdata is stored in fileInfo.)
  • Lines 6-8:
    files, count: store the results from command.cgi with op=100 and op=101 respectively.
    rowdata: manages the file information for the selected rows

FSViewController.m content list acquisition

- (void)getFileList:(NSString *)path{

    NSError *error = nil;

    // Get file list
    // Make url
    NSURL *url100 = [NSURL URLWithString:[@"http://flashair/command.cgi?op=100&DIR=" 
                                                            stringByAppendingString: path]];
    // Run cgi
    NSString *dirStr = [NSString stringWithContentsOfURL:url100 
                                                encoding:NSUTF8StringEncoding error:&error];
    if ([error.domain isEqualToString:NSCocoaErrorDomain]){
        NSLog(@"error100 %@\n",error);
        return;
    }
    files = [dirStr componentsSeparatedByString:@"\n"];

    // Get the number of files
    // Make url
    NSURL *url101 = [NSURL URLWithString:[@"http://flashair/command.cgi?op=101&DIR=" 
                                                            stringByAppendingString: path]];
    // Run cgi
    NSString *cntStr = [NSString stringWithContentsOfURL:url101 
                                                encoding:NSUTF8StringEncoding error:&error];
    if ([error.domain isEqualToString:NSCocoaErrorDomain]) {
        NSLog(@"error101 %@\n",error);
        return;
    }
    count = cntStr;

    // Display results
    self.labelCount.text = [@"Items Found:" stringByAppendingString:cntStr];
    if(![path isEqualToString:@"/"]){
        self.labelDirectory.text = [path stringByAppendingString:@"/" ];
    }else{
        self.labelDirectory.text = @"/";
    }

}
  • Lines 7-16:
    We run command.cgi with op=100 and set the results to the appropriate variables.
  • Lines 20-29:
    We run command.cgi with op=101 and set the results to the appropriate variables.
  • Lines 32-37:
    We set the results to labelDirectory

FSViewController.m (1) sets default columns and rows for the tableView

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.    
    return [count intValue];
}
  • Lines 4, 10:
    We set the number of cells we will have in the Table View.

FSViewController.m (2) sets the text for the tableView

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier 
                                                                    forIndexPath:indexPath];

    // Configure the cell...
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
                                                            reuseIdentifier:CellIdentifier];
    }

    NSString *filename = [[[files objectAtIndex:indexPath.row + 1] 
                                        componentsSeparatedByString:@","] objectAtIndex:1];
    unsigned char attribute = [[[[files objectAtIndex:indexPath.row + 1] 
                                componentsSeparatedByString:@","] objectAtIndex:3] intValue];

    // If it is folder
    if ((attribute & 0x10) != 0) {
        filename = [filename stringByAppendingString:@"/" ];
    }else{
        NSArray *name_array = [filename componentsSeparatedByString:@"."];
        NSString *ext = [[name_array objectAtIndex:[name_array count]-1] lowercaseString];
        if (!([ext isEqualToString:@"jpg"] || [ext isEqualToString:@"jpeg"] ||
              [ext isEqualToString:@"png"] || [ext isEqualToString:@"jpe"])) {
            [cell setUserInteractionEnabled:NO];
        }
    }
    cell.textLabel.text = filename;

    return cell;
}
  • Lines 3-11:
    Get the cell we wish to process.
  • Lines 13-28:
    Get the file name and attributes of a file. Each file will be checked for type and skipped accordingly (if it is not in JPEG format).
  • Line 29:
    textLabel: stores the name of the retrieved file.

Setting Up the Content List's Behavior

We will set up the touch behavior of the content list. We will set behaviors for when the user taps the back button or an image file or directory in the content list.

this image shows the content list

These are the operations we are running when the list is tapped:

FSViewController.m tableView behavior

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Navigation logic may go here. Create and push another view controller.    
    rowdata = [files objectAtIndex:indexPath.row + 1];
    NSString *dir = [[rowdata componentsSeparatedByString:@","] objectAtIndex:0];
    NSString *filename = [[rowdata componentsSeparatedByString:@","] objectAtIndex:1];
    NSString *filePath = [[dir stringByAppendingString:@"/"] stringByAppendingString:filename];

    // If it is folder
    if(([[[rowdata componentsSeparatedByString:@","] objectAtIndex:3] intValue] & 0x10) != 0){
        [self getFileList:filePath];
        [self.tableViewFileList reloadData];
    }else{
        [self performSegueWithIdentifier:@"imageView" sender:self];
    }

    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

}

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Give next View the Data
    if ([segue.identifier isEqualToString:@"imageView"]) {
        FSImageViewController *iamgeViewController = segue.destinationViewController;
        iamgeViewController.fileInfo = rowdata;
    }
}
  • Lines 4-15:
    If a folder is selected, we will again call command.cgi to get the contents of the directory that was selected. We set this to a Table View as well.
  • Lines 24-26:
    We pass the information for the items selected to the View Controller.

Above, we have set the behavior of the back button.

FSViewController.m behavior when pushing a button

- (IBAction)buttonPush:(id)sender {
    NSString *path = [self.labelDirectory.text 
                                substringToIndex:[self.labelDirectory.text length] - 1];
    NSRange found = [path rangeOfString:@"/" options:NSBackwardsSearch];

    if(found.location != NSNotFound){
        if (found.location == 0) {
            path = @"/";
        }else{
            path = [path substringToIndex:found.location];
        }
    }else{
        path = @"/";       
    }

    // Reload tableview  
    [self getFileList:path];
    [self.tableViewFileList reloadData];
}
  • Lines 1-14:
    We create the destination path of the folder we would like to return to.
  • Lines 17-18:
    Based on the path we created in lines 1-14, we will call command.cgi again on this path to show the contents list. We set this to a Table View.

Result

Once we have completed writing the program, we will check to see if it works. If we tap "IMG_2340.JPG" in the content list shown above (in the example Table View screenshot earlier in this tutorial), we should see that image file displayed like this:

this image shows the image view

You have now completed the tutorial on downloading content.

Sample Code

ios_tutorial_03.zip (25KB)

All sample code on this page is licensed under BSD 2-Clause License